#ifndef MYCDIR
#define MYCDIR

#include "..\CDefault\CDefault.hpp"

typedef _stdcall HRESULT (*LPSTDCALLFUN)(WORD, DISPPARAMS FAR*, VARIANT FAR*, EXCEPINFO FAR*);
typedef _cdecl HRESULT (*LPCDECLFUN)(WORD, DISPPARAMS FAR*, VARIANT FAR*, EXCEPINFO FAR*);
typedef _pascal HRESULT (*LPPASCALFUN)(WORD, DISPPARAMS FAR*, VARIANT FAR*, EXCEPINFO FAR*);

class CDir: public CDefault {
	
	HINSTANCE hInstance;
	wstring wsPath;	
	map<long, wstring> names;
	map<long, CDir*> dirs;
	
		FARPROC getFarProc(PCWSTR pcwstr);
	
	public:
		// IDispatch
		STDMETHOD(GetIDsOfNames(REFIID, OLECHAR FAR* FAR*, unsigned int, LCID, DISPID FAR*));
		STDMETHOD(Invoke(DISPID, REFIID, LCID, WORD, DISPPARAMS FAR*, VARIANT FAR*, EXCEPINFO FAR*, unsigned int FAR*));
	
		CDir(wstring);
		~CDir();

};

CDir::CDir(wstring wspath) {
	wsPath = wspath;
	hInstance = NULL;
	int m = wsPath.length();			
	int pos = wsPath.find_last_of(L"\\", m);
	if (pos > 0) {
		wstring wsBaseName = wsPath.substr(pos + 1, m);
		wstring xpath = wsPath;
		xpath.append(L"\\");
		xpath.append(wsBaseName);
		xpath.append(L".dll");		
		if (GetFileAttributesW(xpath.c_str()) != INVALID_FILE_ATTRIBUTES) {
			hInstance = LoadLibraryW(xpath.c_str());
		};
	};
}

CDir::~CDir() {
	
	map<long, CDir*>::iterator it;
	for(it=dirs.begin(); it!=dirs.end(); ++it) {
		it->second->Release();
	}
	
	if (hInstance != NULL) FreeLibrary(hInstance);
}

FARPROC CDir::getFarProc(PCWSTR pcwstr) {
	
	  FARPROC fp = NULL; 
	  unsigned length = wcslen(pcwstr);
	  char* p = (char*)HeapAlloc(
		GetProcessHeap(), 
		HEAP_ZERO_MEMORY,
		length + 1
	  );
  
	  int res = WideCharToMultiByte(
		CP_ACP, 
		0, 
		pcwstr, 
		length, 
		p, 
		length, 
		NULL, 
		NULL
	  );

	  if (res != 0)	{
		 fp = GetProcAddress(hInstance, p);
	  } else {
	  	gErrorMsgW(GetLastError(), const_cast<wchar_t*>(pcwstr), NULL);
	  }
	  
	  HeapFree(GetProcessHeap(), 0, p);
	
	return fp;
}

STDMETHODIMP CDir::GetIDsOfNames( 
  REFIID riid,                  
  OLECHAR FAR* FAR* rgszNames,  
  unsigned int,          
  LCID,                   
  DISPID FAR* rgDispId 
)
{   
  if (riid != IID_NULL) return DISP_E_UNKNOWNINTERFACE;

	map<long, wstring>::iterator it;
	for(it=names.begin(); it!=names.end(); ++it) {
		wstring name = it->second;
		if (wcscmp(name.c_str(), rgszNames[0]) != 0) continue;
		*rgDispId = it->first;
		return S_OK;
	}

  wstring name = rgszNames[0];
  wstring xpath = wsPath;
  xpath.append(L"\\");
  xpath.append(name);
  
  if (GetFileAttributesW(xpath.c_str()) != INVALID_FILE_ATTRIBUTES) {
	*rgDispId = names.size();
	names[*rgDispId] = xpath;
	return S_OK;
  };
  
  if (hInstance != NULL)  {
	  FARPROC fp = getFarProc(name.c_str());
	  if (fp != NULL) {
		*rgDispId = names.size();
		names[*rgDispId] = rgszNames[0];
		return S_OK;
	  };
  };
  
  return DISP_E_UNKNOWNNAME;
}

STDMETHODIMP CDir::Invoke( 
  DISPID  dispIdMember,      
  REFIID  riid,              
  LCID,                
  WORD  wFlags,              
  DISPPARAMS FAR* pDispParams,  
  VARIANT FAR*  pVarResult,  
  EXCEPINFO FAR* pe,  
  unsigned int FAR*)
{   
  if (riid != IID_NULL) return DISP_E_UNKNOWNINTERFACE;

   // Make sure the wFlags are valid.
  if(wFlags & ~(DISPATCH_METHOD | DISPATCH_PROPERTYGET |
     DISPATCH_PROPERTYPUT | DISPATCH_PROPERTYPUTREF))
     return E_INVALIDARG;
     
  if (pDispParams->cNamedArgs > 0) return DISP_E_NONAMEDARGS;
  
  if (dispIdMember >= (DISPID)names.size()) return DISP_E_MEMBERNOTFOUND; 
  
  VARIANT Res;
  VariantInit(&Res);
  if (pVarResult == NULL) pVarResult = &Res;
  
  wstring name = names[dispIdMember];	  
    
  int m = name.length();
  int pos = name.find_last_of(L"\\", m);
  if (pos > 0) {
  	pVarResult->n1.n2.vt = VT_DISPATCH;
  	CDir* xDir = new CDir(name);
  	dirs[dirs.size()] = xDir;
  	xDir->AddRef();
  	pVarResult->n1.n2.n3.pdispVal = dynamic_cast<IDispatch*>(xDir);
  	return S_OK;
  };

  HRESULT hr = DISP_E_MEMBERNOTFOUND;
  	
  if (hInstance != NULL) {
	FARPROC fp = getFarProc(name.c_str());
	BOOL boolNF = TRUE;
	int pos_1, pos_2;
	
	if (fp != NULL) {
		pos_1 = name.find(L"CDECL");
		pos_2 = name.find(L"cdecl");		
		if ((pos_1 == 0)||(pos_2 == 0)) {
			LPCDECLFUN lpFunc = (LPCDECLFUN)fp;
			hr = lpFunc(wFlags, pDispParams, pVarResult, pe);
			boolNF = FALSE;			
		};
		pos_1 = name.find(L"PASCAL");
		pos_2 = name.find(L"pascal");		
		if ((pos_1 == 0)||(pos_2 == 0)) {		
			LPPASCALFUN lpFunc = (LPPASCALFUN)fp;
			hr = lpFunc(wFlags, pDispParams, pVarResult, pe);
			boolNF = FALSE;			
		};
		pos_1 = name.find(L"STDCALL");
		pos_2 = name.find(L"stdcall");		
		if ((pos_1 == 0)||(pos_2 == 0)||boolNF) {				
			LPSTDCALLFUN lpFunc = (LPSTDCALLFUN)fp;
			hr = lpFunc(wFlags, pDispParams, pVarResult, pe);
		};
	};
  };
  
  return hr;
}

#endif MYCDIR
